import { TableEvent } from './table-event';
import { Inject, Injectable } from '../decorators/inject';
import { Config } from './config';
import { NTable } from '../types/table.interface';
import { NExcel } from '../types';
import { EventHelper } from '../utils/event-helper';
import { fromEvent } from 'rxjs';
import { map, takeUntil, concatAll, filter, max } from 'rxjs/operators';
import { TableDatas } from './table-datas';
import { Hook } from './hook';
import { RenderTrigger } from './render-trigger';
import { AddHook } from '../decorators/add-hook';
import './scroll.less';
type TScrollY = {
    barHeight: number,
    maxRow: number,
    scrollRow: number,
    containerEle: HTMLElement,
    barEle: HTMLElement,
    defaultViewRowCount: number;
    defaultMaxRow: number;
};
type RScrollX = {
    barWidth: number,
    maxCol: number,
    scrollCol: number,
    containerEle: HTMLElement,
    barEle: HTMLElement,
    defaultViewColCount: number;
    defaultMaxCol: number;
}
@Injectable('Scroll')
export class Scroll {
    private isScrolling: boolean = false;
    private yParma: TScrollY = {
        barHeight: 40,
        maxRow: 50,
        defaultMaxRow: 50,
        scrollRow: 0,
        containerEle: null,
        barEle: null,
        defaultViewRowCount: 0
    };
    private XParam: RScrollX = {
        barWidth: 40,
        maxCol: 30,
        defaultMaxCol: 30,
        scrollCol: 0,
        containerEle: null,
        barEle: null,
        defaultViewColCount: 0
    }
    constructor(
        protected tableEvent: TableEvent,
        protected conf: Config,
        @Inject(NExcel.OperateContainerToken) protected ele: HTMLElement,
        @Inject(NExcel.CanvasToken) protected canvasEle: HTMLCanvasElement,
        protected tableDatas: TableDatas,
        protected hook: Hook,
        protected renderTrigger: RenderTrigger
    ) {

    }
    public init() {
        return;
        let defaultMaxCol = 500; //Math.ceil((this.canvasEle.clientWidth - this.conf.header.row.width) / (this.conf.header.col.width - 1));
        let defaultMaxRow = 2000; //Math.ceil((this.canvasEle.clientHeight - this.conf.header.col.height) / (this.conf.header.row.height - 1));
        this.XParam.defaultMaxCol = defaultMaxCol + 103;
        this.yParma.defaultMaxRow = defaultMaxRow + 3;
        this.XParam.maxCol = defaultMaxCol + 103;
        this.yParma.maxRow = defaultMaxRow + 3;
        this.yParma.defaultViewRowCount = Math.ceil((this.canvasEle.clientHeight * this.conf.pixelRatio - this.conf.header.col.height) / (this.conf.header.row.height));
        this.XParam.defaultViewColCount = Math.ceil((this.canvasEle.clientWidth * this.conf.pixelRatio - this.conf.header.row.width) / (this.conf.header.col.width))
        this.tableEvent.scrollObs$.subscribe(res => {
            if (res.directive === 'top' || res.directive === 'bottom') {
                if (res.directive == 'bottom') {
                    if (this.yParma.scrollRow + this.yParma.defaultViewRowCount == this.yParma.maxRow) {
                        this.yParma.maxRow += 1;
                    }
                    this.yParma.scrollRow++;
                } else {
                    if (this.yParma.scrollRow == 0) {
                        return;
                    }
                    if (this.tableDatas.getMaxRow() < this.yParma.maxRow && this.yParma.maxRow > 50) {
                        this.yParma.maxRow--;
                    }
                    this.yParma.scrollRow--;
                }
                this.yParma.barHeight = (this.yParma.defaultViewRowCount / this.yParma.maxRow) * this.yParma.containerEle.clientHeight;
                this.yParma.barEle.style.height = this.yParma.barHeight + 'px';
                this.yParma.barEle.style.top = String((this.yParma.scrollRow / this.yParma.maxRow) * this.yParma.containerEle.clientHeight) + 'px';
                this.conf.view.row = res.height;
            } else {
                if (res.directive === 'right') {
                    // console.log(this.XParam.defaultViewColCount,this.yParma.defaultViewRowCount)
                    if (this.XParam.scrollCol + this.XParam.defaultViewColCount === this.XParam.maxCol) {
                        this.XParam.maxCol += 1;
                    }
                    this.XParam.scrollCol++;
                } else {
                    if (this.XParam.scrollCol === 0) {
                        return;
                    }
                    if (this.tableDatas.getMaxCol() < this.XParam.maxCol && this.XParam.maxCol > 50) {
                        this.XParam.maxCol--;
                    }
                    this.XParam.scrollCol--;
                }
                this.XParam.barWidth = (this.XParam.defaultViewColCount / this.XParam.maxCol) * this.XParam.containerEle.clientWidth;
                this.XParam.barEle.style.width = this.XParam.barWidth + 'px';
                this.XParam.barEle.style.left = String((this.XParam.scrollCol / this.XParam.maxCol) * this.XParam.containerEle.clientWidth) + 'px';
                this.conf.view.col = res.width;
            }
            this.renderTrigger.triggerRenderAll();
        });
        this.createYScroll();
        this.createXScroll();
        this.initEffectiveBoundary();
        this.initSubscripeEvent();
        this.hook.afterChangeFreezeRow.add(() => {
            this.yParma.barEle.style.top = '0px';
        });
        this.hook.afterChangeFreezeCol.add(() => {
            this.XParam.barEle.style.left = '0px'
        });
    }
    protected initEffectiveBoundary() {
        this.initYBarStatus();
        this.initXBarStatus();
    }
    protected initXBarStatus() {
        let viewCounCol = Math.ceil((this.XParam.containerEle.clientWidth - this.conf.header.row.width) / (this.conf.header.col.width - 1));
        let barWidth = (viewCounCol / this.XParam.maxCol) * this.canvasEle.clientWidth;
        if (barWidth > 40) {
            this.XParam.barWidth = barWidth;
        } else {
            this.XParam.barWidth = 40;
        }
        this.XParam.barEle.style.width = this.XParam.barWidth + 'px';
    }
    protected initYBarStatus() {
        let viewCountRow = Math.ceil((this.yParma.containerEle.clientHeight - this.conf.header.col.height) / (this.conf.header.row.height));
        let barHeight = (viewCountRow / this.yParma.maxRow) * this.canvasEle.clientHeight;
        if (barHeight > 40) {
            this.yParma.barHeight = barHeight;
        } else {
            this.yParma.barHeight = 40;
        }
        this.yParma.barEle.style.height = this.yParma.barHeight + 'px';
    }
    protected initSubscripeEvent() {
        this.tableEvent.render$.subscribe(res => {
            if (res.type != 'afterRender' || this.isScrolling == true) return;
        });
    }
    protected bindScrollEvent(scrollBar: HTMLElement, downCallback: () => void, upCallback: () => void, callback: (res: { x: number, y: number }) => void) {
        const mouseDown = fromEvent<any>(scrollBar, "mousedown")
            .pipe(map(evt => {
                this.isScrolling = true;
                downCallback();
                return evt;
            }))
        const mouseMove = fromEvent<any>(document, "mousemove");
        const mouseUp: any = fromEvent<any>(document, "mouseup")
            .pipe(map(evt => {
                this.isScrolling = false;
                upCallback();
                this.renderTrigger.triggerRenderAll();
                return evt;
            }));
        mouseDown
            .pipe(
                map((e: any) => {
                    const top = e.target.style.top.replace('px', '');
                    const left = e.target.style.left.replace('px', '');
                    const clickOffsetX = e.clientX - left;
                    const clickOffsetY = e.clientY - top;
                    return {
                        x: clickOffsetX,
                        y: clickOffsetY
                    };
                }),
                map((postion: any) => {
                    return mouseMove.pipe(
                        takeUntil(mouseUp),
                        map((moveEvent: MouseEvent) => {
                            return {
                                x: moveEvent.clientX - postion.x,
                                y: moveEvent.clientY - postion.y
                            }
                        })
                    );
                }),
                concatAll()
            ).subscribe(res => {
                callback(res);
                this.renderTrigger.triggerRenderAll();
            });
    }
    protected createYScroll() {
        let scrollContainer = document.createElement('div');
        scrollContainer.className = 'z-table-scroll-y';
        let scrollBar = document.createElement('div');
        scrollBar.className = 'z-table-scroll-bar';
        scrollContainer.appendChild(scrollBar);
        this.ele.appendChild(scrollContainer);

        scrollBar.setAttribute('style', `top: 0px;left: 0px;height: ${this.yParma.barHeight}px`);
        this.yParma.barEle = scrollBar;
        this.yParma.containerEle = scrollContainer;
        this.bindScrollEvent(
            scrollBar,
            () => {
                scrollBar.style.transition = 'none';
            },
            () => {
                // 滚动条 滚动视觉效果
                // scrollBar.style.transition = '0.2s all';
                let tableMaxRow = this.tableDatas.getMaxRow();
                if (tableMaxRow < this.yParma.maxRow && this.yParma.maxRow > this.yParma.defaultMaxRow) {
                    if (this.yParma.scrollRow > this.yParma.defaultMaxRow) {
                        this.yParma.maxRow = this.yParma.scrollRow + this.yParma.defaultViewRowCount;
                    } else {
                        this.yParma.maxRow = this.yParma.scrollRow + this.yParma.defaultViewRowCount;
                    }
                }
                this.yParma.barHeight = (this.yParma.defaultViewRowCount / this.yParma.maxRow) * this.yParma.containerEle.clientHeight;
                this.yParma.barEle.style.height = this.yParma.barHeight + 'px';
                this.yParma.barEle.style.top = String((this.yParma.scrollRow / this.yParma.maxRow) * this.yParma.containerEle.clientHeight) + 'px';
            },
            (res) => {
                if (res.y > 0) {
                    scrollBar.style.top = res.y + 'px';
                    let barHeight = Number(scrollBar.clientHeight);
                    let containerHeight = Number(scrollContainer.clientHeight);
                    if (barHeight + res.y > containerHeight) {
                        scrollBar.style.top = String(containerHeight - barHeight) + 'px';
                    } else {
                        scrollBar.style.top = res.y + 'px';
                    }
                } else {
                    scrollBar.style.top = '0px';
                }
                this.yParma.scrollRow = Math.round(Number(this.yParma.barEle.style.top.replace('px', '')) / this.yParma.containerEle.clientHeight * this.yParma.maxRow);
                let viewTop = 0;
                for (let i = 0; i < this.yParma.scrollRow; i++) {
                    // console.log(this.tableDatas.getRowHeight(i))
                    viewTop += this.tableDatas.getRowHeight(i);
                }
                // console.log('----')
                this.conf.view.row = viewTop;
                this.renderTrigger.triggerRenderAll();
            }
        );
    }
    protected createXScroll() {
        let scrollContainer = document.createElement('div');
        scrollContainer.className = 'z-table-scroll-x';
        let scrollBar = document.createElement('div');
        scrollBar.className = 'z-table-scroll-bar'
        scrollContainer.appendChild(scrollBar);
        this.ele.appendChild(scrollContainer);
        scrollBar.setAttribute('style', 'top: 0px;left: 0px;');
        this.XParam.barEle = scrollBar;
        this.XParam.containerEle = scrollContainer;
        this.bindScrollEvent(
            scrollBar,
            () => {
                scrollBar.style.transition = 'none';
            },
            () => {
                scrollBar.style.transition = '0.2s all';
            },
            (res) => {
                if (res.x > 0) {
                    scrollBar.style.left = res.x + 'px';
                    let barHeight = Number(scrollBar.clientWidth);
                    let containerWidth = Number(scrollContainer.clientWidth) - 6;
                    if (barHeight + res.x > containerWidth) {
                        scrollBar.style.left = String(containerWidth - barHeight) + 'px';
                    } else {
                        scrollBar.style.left = res.x + 'px';
                    }
                } else {
                    scrollBar.style.left = '0px';
                }
                const left = Number(this.XParam.barEle.style.left.replace('px', ''));
                this.XParam.scrollCol = Math.round(left / this.XParam.containerEle.clientWidth * this.XParam.maxCol);
                let viewLeft = 0;
                for (let i = this.conf.freeze.col + 1; i < this.XParam.scrollCol; i++) {
                    viewLeft += this.tableDatas.getColWidth(i);
                }
                this.conf.view.col = viewLeft;
                this.renderTrigger.triggerRenderAll();
            }
        );
    }
    @AddHook('afterScaleChange')
    @AddHook('pixelRatioChange')
    protected viewChange() {
        let viewTop = 0;
        for (let i = 0; i < this.yParma.scrollRow; i++) {
            viewTop += this.tableDatas.getRowHeight(i);
        }
        this.conf.view.row = viewTop;
        let viewLeft = 0;
        for (let i = this.conf.freeze.col + 1; i < this.XParam.scrollCol; i++) {
            viewLeft += this.tableDatas.getColWidth(i);
        }
        this.conf.view.col = viewLeft;
    }
}

